package internal

import (
	"github.com/xiyoufang/cloud-syncer/internal/config"
	"github.com/xiyoufang/cloud-syncer/internal/db"
	"github.com/xiyoufang/cloud-syncer/internal/log"
	"github.com/xiyoufang/cloud-syncer/internal/syncer"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// FileScanner 文件扫描器
type FileScanner struct {
	paths []struct {
		Local  string
		Remote string
	}
	interval   time.Duration // 扫描中间的延时
	excludes   []string
	repository db.Repository
	syncer     syncer.Syncer
	logger     *log.Logger
}

func NewFileScanner(sync config.SyncConfig, repository db.Repository, syncer syncer.Syncer, logger *log.Logger) *FileScanner {
	return &FileScanner{
		paths:      sync.Paths,
		interval:   time.Duration(sync.Interval) * time.Second,
		excludes:   sync.Excludes,
		repository: repository,
		syncer:     syncer,
		logger:     logger,
	}
}

func (s *FileScanner) Start() {
	for {
		startTime := time.Now()
		if affected, err := s.repository.TruncateTempTable(); err != nil {
			s.logger.Error("清空临时表出错: %v", err)
		} else {
			s.logger.Info("清空临时表，影响条数: %d", affected)
		}
		if err := s.repository.Vacuum(); err != nil {
			s.logger.Error("执行Vacuum命令出错, 错误信息: %v", err)
		}
		var files []db.File
		for _, path := range s.paths {
			if len(path.Local) > 0 && len(path.Remote) > 0 {
				s.logger.Info("扫描文件, 本地路径: %s, 远程路径: %s", path.Local, path.Remote)
				src := strings.TrimSpace(path.Local)
				dest := strings.TrimSpace(path.Remote)
				// 遍历源文件目录
				err := filepath.Walk(src, func(srcPath string, info os.FileInfo, err error) error {
					if err != nil {
						s.logger.Error("访问路径时出错: %v\n", err)
						return err
					}
					if !info.IsDir() && !s.shouldExclude(srcPath, s.excludes) {
						absSrcPath, err := filepath.Abs(srcPath)
						if err != nil {
							s.logger.Error("获取绝对路径时出错: %v\n", err)
							return err
						}
						// 获取相对路径
						relativePath, err := filepath.Rel(src, srcPath)
						if err != nil {
							s.logger.Error("获取相对路径时出错: %v\n", err)
							return err
						}
						destPath := filepath.ToSlash(filepath.Join(dest, relativePath)) // 拼接目标路径
						file := db.File{
							Name:       info.Name(),
							Size:       info.Size(),
							Modified:   info.ModTime().UnixMilli(),
							LocalPath:  absSrcPath,
							RemotePath: destPath,
						}
						files = append(files, file)
						if len(files) >= db.BatchSize {
							if insertSize, err := s.repository.InsertBatchFiles(files); err != nil {
								s.logger.Error("批量插入扫描到的文件数据失败: %v", err)
							} else {
								s.logger.Info("成功插入%d条数据到临时表", insertSize)
							}
							files = nil // 清空切片以便下次使用
						}
					}
					return nil
				})

				if err != nil {
					s.logger.Error("扫描文件时出错: %v", err)
				}
			} else {
				s.logger.Error("备份路径配置错误, 本地路径: %s, 远程路径: %s", path.Local, path.Remote)
			}
		}
		if len(files) > 0 {
			if insertSize, err := s.repository.InsertBatchFiles(files); err != nil {
				s.logger.Error("批量插入扫描到的文件数据失败: %v", err)
			} else {
				s.logger.Info("成功插入%d条数据到临时表", insertSize)
			}
		}
		s.logger.Info("开始合并数据, 并生成同步任务")
		if err := s.repository.MergeFilesAndInsertHistories(); err != nil {
			s.logger.Error("执行数据合并错误: %v", err)
		}
		err := s.pushLocalFilesToRemote()
		if err != nil {
			s.logger.Error("上传本地文件失败, 错误信息: %v", err)
		}
		s.logger.Info("备份文件耗时: %v, %v 秒后进入下一次扫描", time.Since(startTime), s.interval)
		time.Sleep(s.interval)
	}
}

func (s *FileScanner) pushLocalFilesToRemote() error {
	for {
		tasks, err := s.repository.GetPendingHistories(100)
		if err != nil {
			return err
		}
		if len(tasks) == 0 {
			return nil
		}
		s.pushTasks(tasks)
	}
}

func (s *FileScanner) shouldExclude(srcPath string, excludes []string) bool {
	for _, pattern := range excludes {
		matched, _ := filepath.Match(pattern, filepath.Base(srcPath))
		if matched {
			return true
		}
	}
	return false
}

func (s *FileScanner) pushTasks(tasks []db.Task) {
	var wg sync.WaitGroup
	sem := make(chan struct{}, 10) // 限制并发数量为 10
	for _, task := range tasks {
		wg.Add(1)
		sem <- struct{}{} // 占用一个信号量
		go func() {
			defer func() {
				wg.Done()
				<-sem // 释放信号量
			}()
			err := s.syncer.PushFile(task.LocalPath, task.RemotePath)
			if err != nil {
				s.logger.Error("文件上传失败，本地路径: %s, 远程路径: %s, 错误信息: %v", task.LocalPath, task.RemotePath, err)
			} else {
				s.logger.Info("文件上传成功，本地路径: %s, 远程路径: %s", task.LocalPath, task.RemotePath)
			}
			if err != nil { // 上传错误
				_, err = s.repository.UpdateHistorySyncStatus(task.Id, 2, err.Error())
				if err != nil {
					s.logger.Error("更新同步状态失败: %v", err)
				}
			} else {
				_, err = s.repository.UpdateHistorySyncStatus(task.Id, 1, "")
				if err != nil {
					s.logger.Error("更新同步状态失败: %v", err)
				}
			}
		}()
	}
	wg.Wait()
}
